#pragma once
#include "CameraPrecompiled.hxx"

#define ArcballDefaultRadius (0.9f)

namespace Eifersucht
{
	class Arcball
	{
#pragma region Internal shared types

	public_shared_access: // Internal shared types

		struct Int16x2
		{
			union
			{
				struct
				{
					Int16 x;
					Int16 y;
				};
				struct
				{
					Int16 _1;
					Int16 _2;
				};
			};
		};

		typedef DirectX::XMINT2 Int32x2;		// Arc ball int x 2
		typedef Reign::Vector2 Float32x2;		// Arc ball float x 2
		typedef Reign::Vector3 Float32x3;		// Arc ball float x 3
		typedef Reign::Matrix Float32x4x4;		// Arc ball float x 16
		typedef Reign::Quaternion Quaternion;	// Arc ball float x 4

		TinyTypeDecl(Int16x2);					// Arc ball uint x 2
		TinyTypeDecl(Int32x2);					// Arc ball uint x 2
		TinyTypeDecl(Float32x2);				// Arc ball float x 2
		TinyTypeDecl(Float32x3);				// Arc ball float x 3
		TinyTypeDecl(Quaternion);				// Arc ball float x 4
		TinyTypeDecl(Float32x4x4);				// Arc ball float x 16

#pragma endregion Internal shared types

#pragma region Properties

	public_shared_access: // Properties

		REIGN_FORCEINLINING Float32x4x4ConstPtr GetTranslationDeltaMatrix() { return &m_f4x4TranslationDelta; }
		REIGN_FORCEINLINING Float32x4x4ConstPtr GetTranslationMatrix() { return &m_f4x4Translation; }
		REIGN_FORCEINLINING Float32x4x4ConstPtr GetRotationMatrix() { m_f4x4Rotation = DirectX::XMMatrixRotationQuaternion(m_f4Now); return &m_f4x4Rotation; }

		REIGN_FORCEINLINING Void SetNowQuat( _In_ QuaternionConstPtr pf4Now ) { m_f4Now = (*pf4Now); }
		REIGN_FORCEINLINING Void SetNowQuat( _In_ QuaternionConstRef pf4Now ) { m_f4Now = pf4Now; }
		REIGN_FORCEINLINING QuaternionConstPtr GetNowQuat() const { return &m_f4Now; }

		REIGN_FORCEINLINING Void SetTranslationRadius( _In_ Float32 fRadius ) { m_fTranslationRadius = fRadius; }
		REIGN_FORCEINLINING Bool IsBeingDragged() const { return m_bDragging; }

		REIGN_HINTINLINING Void SetWindow( _In_ Int32 iWidth, _In_ Int32 iHeight, _In_ Float32 fRadius )
		{
			m_fRadius = fRadius;
			m_iWindowWidth = iWidth; 
			m_iWindowHeight = iHeight; 
			m_f2Center.x = iWidth / 2.0f;
			m_f2Center.y = iHeight / 2.0f;
		}

		REIGN_HINTINLINING Void SetWindow( _In_ Int16 iWidth, _In_ Int16 iHeight )
		{
			m_iWindowWidth = iWidth; 
			m_iWindowHeight = iHeight; 
			m_f2Center.x = iWidth / 2.0f;
			m_f2Center.y = iHeight / 2.0f;
			m_fRadius = ArcballDefaultRadius;
		}

		REIGN_HINTINLINING Void SetOffset( _In_ Int32x2ConstRef i2Offset )
		{
			m_i2Offset.x = i2Offset.x;
			m_i2Offset.y = i2Offset.y;
		}

		REIGN_HINTINLINING Void SetOffset( _In_ Int16 iLeft, _In_ Int16 iTop )
		{
			m_i2Offset.x = iLeft;
			m_i2Offset.y = iTop;
		}

#pragma endregion Properties

#pragma region Members

	protected_shared_access: // Members
		
		_Inout_ Int16x2 m_i2Offset;						// [Get/Set] Arc ball window top left point
		_Inout_ Int32 m_iWindowWidth;					// [Get/Set] Arc ball window width
		_Inout_ Int32 m_iWindowHeight;					// [Get/Set] Arc ball window height

		_Inout_ Float32 m_fRadius;						// [Get/Set] Arc ball radius in screen coords
		_Inout_ Float32 m_fTranslationRadius;			// [Get/Set] Arc ball radius for target translation
		_Out_ Float32x2 m_f2Center;						// [Get] Arc ball center

		_Inout_ Quaternion m_f4Now;						// [Get/Set] Arc ball quat while dragging event
		_In_ Quaternion m_f4Last;						// [Set] Arc ball quat before last mouse event
		_In_ Bool m_bDragging;							// [Set] Indicates whether a user is dragging an arc ball

		_In_ Int16x2 m_i2LastMousePosition;				// [Set] Arc ball last mouse point position
		_In_ Float32x3 m_f3StartPosition;				// [Set] Arc ball start rotation point
		_In_ Float32x3 m_f3EndPosition;					// [Set] Arc ball current rotation point

		_Inout_ Float32x4x4 m_f4x4TranslationDelta;		// [Get/Set] Arc ball position delta
		_Inout_ Float32x4x4 m_f4x4Translation;			// [Get/Set] Arc ball position
		_Inout_ Float32x4x4 m_f4x4Rotation;				// [Get/Set] Arc ball orientation

		Float32x3 Convert( _In_ Float32 fXScreen, _In_ Float32 fYScreen );

#pragma endregion Members

	public_shared_access:

		Arcball();
		Void Reset();

		Void OnDragStart( _In_ Int16 iXPtr, _In_ Int16 iYPtr );
		static Quaternion REIGN_METHODSTDCALLTYPE QuaternionFromBallPoints(
			_In_ Float32x3ConstPtr cpf3From, 
			_In_ Float32x3ConstPtr cpf3To
			);

		REIGN_HINTINLINING Void OnDragContinue( _In_ Int16 iXPtr, _In_ Int16 iYPtr )
		{
			if ( m_bDragging )
			{
				//PrintToDebugWindowOnly8( "Eifersucht::Arcball: OnDragContinue." );

				m_f3EndPosition = Convert(
					_To_float iXPtr, 
					_To_float iYPtr
					);

				m_f4Now = m_f4Last * QuaternionFromBallPoints( 
					(&m_f3StartPosition), 
					(&m_f3EndPosition) 
					);
			}
		}

		REIGN_HINTINLINING Void OnDragStop()
		{
			m_bDragging = false;
		}

		REIGN_HINTINLINING static Quaternion REIGN_METHODSTDCALLTYPE QuaternionFromBallPoints(
			_In_ Float32x3ConstRef crf3From, 
			_In_ Float32x3ConstRef crf3To
			)
		{
			return QuaternionFromBallPoints( 
				&crf3From, 
				&crf3To
				);
		}


	};
};